#include "MemStream.h"
#include <stdio.h>
#include <string.h>
#include  <assert.h>
#ifndef WIN32
#include  <stddef.h>
#include <stdlib.h>
#endif

#ifndef ASSERT
#define ASSERT(_x_) assert(_x_)
#endif //ASSERT

#ifndef NULL
#define NULL 0
#endif
//==============================================================================
// CMemStream

bool MmIsValidAddress(void* lp, unsigned int size, bool bReadWrite = true)
{
/*
	if (bReadWrite)
	{
		return !IsBadReadPtr(lp, size) && !IsBadWritePtr(lp, size);
	}
	else
	{
		return !IsBadReadPtr(lp, size);
	}
*/
	return true;
}

CMemStream::CMemStream(unsigned int nGrowBytes/*=1024*/)
{
	m_growSize = nGrowBytes;
	m_position = 0;
	m_bufferSize = 0;
	m_fileSize = 0;
	m_pBuffer = NULL;
	m_autoDelete = true;
}

CMemStream::CMemStream(void* pBuffer, unsigned int bufferSize)
{
	if (pBuffer == NULL || bufferSize == 0)
	{
// 		OutputDebugStringA("Invalid Argument Exception.\n");
	}

	m_growSize = 0;
	m_position = 0;
	m_bufferSize = bufferSize;
	m_fileSize = bufferSize;
	m_pBuffer = (unsigned char*)pBuffer;
	m_autoDelete = false;
}

void CMemStream::Attach(void* pBuffer, unsigned int bufferSize)
{
/*
	if (pBuffer == NULL && bufferSize != 0)
	{
		dbgprint("Invalid Argument Exception.\n");
	}

	ASSERT(m_pBuffer == NULL);
*/
	Close();

	m_position = 0;
	m_bufferSize = bufferSize;
	m_fileSize = bufferSize;
	m_pBuffer = (unsigned char*)pBuffer;
	m_autoDelete = false;
}

void* CMemStream::Detach()
{
	unsigned char* pBuffer = m_pBuffer;

	m_pBuffer = NULL;
	m_fileSize = 0;
	m_bufferSize = 0;
	m_position = 0;
	m_autoDelete = true;

	return pBuffer;
}

CMemStream::~CMemStream()
{
	if (m_pBuffer != NULL)
		Close();

	ASSERT(m_pBuffer == NULL);

	m_growSize = 0;
	m_position = 0;
	m_bufferSize = 0;
	m_fileSize = 0;
}

unsigned char* CMemStream::Alloc(unsigned int size)
{
	return (unsigned char*)malloc(size);
}

unsigned char* CMemStream::Realloc(unsigned char* pBuffer, unsigned int size)
{
	ASSERT(size > 0);
	return (unsigned char*)realloc(pBuffer, size);
}

unsigned char* CMemStream::Memcpy(unsigned char* pDest, const unsigned char* pSrc, unsigned int size)
{
	ASSERT(pDest != NULL);
	ASSERT(pSrc != NULL);

	ASSERT(MmIsValidAddress(pDest, size));
	ASSERT(MmIsValidAddress((void*)pSrc, size, false));

	//memcpy_s(pDest, size, pSrc, size);
	memcpy(pDest, pSrc, size);

	return pDest;
}

void CMemStream::Free(unsigned char* pBuffer)
{
	ASSERT(pBuffer != NULL);

	free(pBuffer);
}

void CMemStream::Grow(unsigned int newSize)
{
	if (m_autoDelete && newSize > m_bufferSize)
	{
		unsigned int newBufferSize = m_bufferSize;

		ASSERT(m_growSize != 0);
		if (m_growSize == 0)
		{
// 			OutputDebugStringA("Memory Exception.\n");
		}

		while (newBufferSize < newSize)
			newBufferSize += m_growSize;

		unsigned char* lpNew;
		if (m_pBuffer == NULL)
			lpNew = Alloc(newBufferSize);
		else
			lpNew = Realloc(m_pBuffer, newBufferSize);

		if (lpNew == NULL)
		{
// 			OutputDebugStringA("Memory Exception.\n");
		}

		m_pBuffer = lpNew;
		m_bufferSize = newBufferSize;
	}
}

unsigned int CMemStream::GetSize() const
{
	return m_fileSize;
}

void CMemStream::SetSize(unsigned int newSize)
{
	if (newSize == m_fileSize)
		return;

	if (newSize > m_bufferSize)
		Grow(newSize);

	if (newSize > m_bufferSize)
		newSize = m_bufferSize;

	if (m_position > newSize)
		m_position = newSize;

	m_fileSize = newSize;
}

unsigned int CMemStream::Read(void* pBuffer, unsigned int size)
{
	if (size == 0)
		return 0;

	ASSERT(pBuffer != NULL);

	if (pBuffer == NULL)
	{
// 		OutputDebugStringA("Invalid Arg Exception.\n");
	}

	ASSERT(MmIsValidAddress(pBuffer, size));

	if (m_position >= m_fileSize)
		return 0;

	unsigned int nread;
	if ((m_position + size) > m_fileSize || (m_position + size) < m_position)
		nread = (unsigned int)(m_fileSize - m_position);
	else
		nread = size;

	Memcpy((unsigned char*)pBuffer, (unsigned char*)m_pBuffer + m_position, nread);

	m_position += nread;

	return nread;
}

unsigned int CMemStream::Pick(void* pBuffer, unsigned int size)
{
	if (size == 0)
		return 0;

	ASSERT(pBuffer != NULL);

	if (pBuffer == NULL)
	{
// 		OutputDebugStringA("Invalid Arg Exception.\n");
	}

	ASSERT(MmIsValidAddress(pBuffer, size));

	if (m_position >= m_fileSize)
		return 0;

	unsigned int nread;
	if ((m_position + size) > m_fileSize || (m_position + size) < m_position)
		nread = (unsigned int)(m_fileSize - m_position);
	else
		nread = size;

	Memcpy((unsigned char*)pBuffer, (unsigned char*)m_pBuffer + m_position, nread);

	return nread;
}

unsigned int CMemStream::Write(const void* pBuffer, unsigned int size)
{
	if (size == 0)
		return 0;

	ASSERT(pBuffer != NULL);
	ASSERT(MmIsValidAddress((void*)pBuffer, size, false));

	if (pBuffer == NULL)
	{
// 		OutputDebugStringA("Invalid Arg Exception.\n");
	}

	if ((m_position + size) > m_bufferSize)
		Grow(m_position + size);

	if ((m_position + size) > m_bufferSize)
	{
		size = (m_position < m_bufferSize) ? (m_bufferSize - m_position) : 0;
	}

	Memcpy(m_pBuffer + m_position, (unsigned char*)pBuffer, size);

	m_position += size;
	if (m_position > m_fileSize)
		m_fileSize = m_position;

	return size;
}

unsigned int CMemStream::Seek(int offset, int whence)
{
	ASSERT(whence == SEEK_SET || whence == SEEK_END || whence == SEEK_CUR);

	long lNewPos = m_position;

	if (whence == SEEK_SET)
	{
		lNewPos = offset;
	}
	else if (whence == SEEK_CUR)
	{
		lNewPos += offset;
	}
	else if (whence == SEEK_END)
	{
		if (offset > 0)
		{
// 			OutputDebugStringA("Bad Seek Exception.\n");
		}
		lNewPos = m_fileSize + offset;
	}
	else
		return m_position;

	if (lNewPos < 0)
	{
		lNewPos = 0;
	}

	//if (static_cast<unsigned int>(lNewPos) > m_fileSize)
	//	Grow((unsigned int)lNewPos);

	m_position = (unsigned int)lNewPos;
	if (m_position > m_fileSize)
		m_position = m_fileSize;

	return m_position;
}

void CMemStream::Flush()
{
}

void CMemStream::Close()
{
/*
	ASSERT((m_pBuffer == NULL && m_bufferSize == 0) ||
		!m_autoDelete ||
		MmIsValidAddress(m_pBuffer, (unsigned int)m_bufferSize, false));

	ASSERT(m_fileSize <= m_bufferSize);
*/

	m_position = 0;
	m_bufferSize = 0;
	m_fileSize = 0;
	if (m_pBuffer != NULL && m_autoDelete)
		Free(m_pBuffer);
	m_pBuffer = NULL;
	m_autoDelete = true;
}

void CMemStream::Abort()
{
	Close();
}

bool CMemStream::ReadInt8(int8_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(int8_t));
	return (nread == sizeof(int8_t));
}

bool CMemStream::ReadUint8(uint8_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(uint8_t));
	return (nread == sizeof(uint8_t));
}

bool CMemStream::ReadInt16(int16_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(int16_t));
	return (nread == sizeof(int16_t));
}

bool CMemStream::ReadUint16(uint16_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(uint16_t));
	return (nread == sizeof(uint16_t));
}

bool CMemStream::ReadInt32(int32_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(int32_t));
	return (nread == sizeof(int32_t));
}

bool CMemStream::ReadUint32(uint32_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(uint32_t));
	return (nread == sizeof(uint32_t));
}

bool CMemStream::ReadInt64(int64_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(int64_t));
	return (nread == sizeof(int64_t));
}

bool CMemStream::ReadUint64(uint64_t* pDst)
{
	unsigned int nread = Read(pDst, sizeof(uint64_t));
	return (nread == sizeof(uint64_t));
}

bool CMemStream::ReadSingle(float* pDst)
{
	unsigned int nread = Read(pDst, sizeof(float));
	return (nread == sizeof(float));
}

bool CMemStream::ReadDouble(double* pDst)
{
	unsigned int nread = Read(pDst, sizeof(double));
	return (nread == sizeof(double));
}

bool CMemStream::ReadSmallString(char* psz, int len)
{
	uint8_t slen = 0;
	if (!ReadUint8(&slen))
		return false;
	if (slen >= len)
		return false;
	unsigned int size = slen * sizeof(char);
	unsigned int nread = Read(psz, size);
	if (nread != size)
		return false;
	psz[slen] = 0;
	return true;
}

bool CMemStream::ReadShortString(char* psz, int len)
{
	uint16_t slen = 0;
	if (!ReadUint16(&slen))
		return false;
	if (slen >= len)
		return false;
	unsigned int size = slen * sizeof(char);
	unsigned int nread = Read(psz, size);
	if (nread != size)
		return false;
	psz[slen] = 0;
	return true;
}

bool CMemStream::ReadString(char* psz, int len)
{
    if (len < 0)
        return false;
	uint32_t slen = 0;
	if (!ReadUint32(&slen))
		return false;
	if (slen >= (uint32_t)len)
		return false;
	unsigned int size = slen * sizeof(char);
	unsigned int nread = Read(psz, size);
	if (nread != size)
		return false;
	psz[slen] = 0;
	return true;
}

// bool CMemStream::ReadSmallCString(CString& str)
// {
// 	uint8_t slen = 0;
// 	if (!ReadUint8(&slen))
// 		return false;
// 	char* psz = str.GetBuffer(slen);
// 	unsigned int size = slen * sizeof(char);
// 	unsigned int nread = Read(psz, size);
// 	if (nread != size)
// 	{
// 		str.ReleaseBuffer(0);
// 		return false;
// 	}
// 	else
// 	{
// 		str.ReleaseBuffer(slen);
// 		return true;
// 	}
// }

// bool CMemStream::ReadShortCString(CString& str)
// {
// 	uint16_t slen = 0;
// 	if (!ReadUint16(&slen))
// 		return false;
// 	char* psz = str.GetBuffer(slen);
// 	unsigned int size = slen * sizeof(char);
// 	unsigned int nread = Read(psz, size);
// 	if (nread != size)
// 	{
// 		str.ReleaseBuffer(0);
// 		return false;
// 	}
// 	else
// 	{
// 		str.ReleaseBuffer(slen);
// 		return true;
// 	}
// }
// 
// bool CMemStream::ReadCString(CString& str)
// {
// 	uint32_t slen = 0;
// 	if (!ReadUint32(&slen))
// 		return false;
// 	char* psz = str.GetBuffer(slen);
// 	unsigned int size = slen * sizeof(char);
// 	unsigned int nread = Read(psz, size);
// 	if (nread != size)
// 	{
// 		str.ReleaseBuffer(0);
// 		return false;
// 	}
// 	else
// 	{
// 		str.ReleaseBuffer(slen);
// 		return true;
// 	}
// }

bool CMemStream::WriteInt8(int8_t src)
{
	unsigned int nwrite = Write(&src, sizeof(int8_t));
	return (nwrite == sizeof(int8_t));
}

bool CMemStream::WriteUint8(uint8_t src)
{
	unsigned int nwrite = Write(&src, sizeof(uint8_t));
	return (nwrite == sizeof(uint8_t));
}

bool CMemStream::WriteInt16(int16_t src)
{
	unsigned int nwrite = Write(&src, sizeof(int16_t));
	return (nwrite == sizeof(int16_t));
}

bool CMemStream::WriteUint16(uint16_t src)
{
	unsigned int nwrite = Write(&src, sizeof(uint16_t));
	return (nwrite == sizeof(uint16_t));
}

bool CMemStream::WriteInt32(int32_t src)
{
	unsigned int nwrite = Write(&src, sizeof(int32_t));
	return (nwrite == sizeof(int32_t));
}

bool CMemStream::WriteUint32(uint32_t src)
{
	unsigned int nwrite = Write(&src, sizeof(uint32_t));
	return (nwrite == sizeof(uint32_t));
}

bool CMemStream::WriteInt64(int64_t src)
{
	unsigned int nwrite = Write(&src, sizeof(int64_t));
	return (nwrite == sizeof(int64_t));
}

bool CMemStream::WriteUint64(uint64_t src)
{
	unsigned int nwrite = Write(&src, sizeof(uint64_t));
	return (nwrite == sizeof(uint64_t));
}

bool CMemStream::WriteSingle(float src)
{
	unsigned int nwrite = Write(&src, sizeof(float));
	return (nwrite == sizeof(float));
}

bool CMemStream::WriteDouble(double src)
{
	unsigned int nwrite = Write(&src, sizeof(double));
	return (nwrite == sizeof(double));
}

bool CMemStream::WriteSmallString(const char* psz, int len/*=-1*/)
{
	if (len < 0)
		len = strlen(psz);
	if (len > 0xff)
		return false;
	if (!WriteUint8((uint8_t)len))
		return false;
	unsigned int size = len * sizeof(char);
	if (Write(psz, size) != size)
		return false;
	return true;
}

bool CMemStream::WriteShortString(const char* psz, int len/*=-1*/)
{
	if (len < 0)
		len = strlen(psz);
	if (len > 0xffff)
		return false;
	if (!WriteUint16((uint16_t)len))
		return false;
	unsigned int size = len * sizeof(char);
	if (Write(psz, size) != size)
		return false;
	return true;
}

bool CMemStream::WriteString(const char* psz, int len/*=-1*/)
{
	if (len < 0)
		len = strlen(psz);
	if (!WriteUint32((uint32_t)len))
		return false;
	unsigned int size = len * sizeof(char);
	if (Write(psz, size) != size)
		return false;
	return true;
}

// bool CMemStream::WriteSmallCString(const CString& str)
// {
// 	if (str.GetLength() > 0xff)
// 		return false;
// 	if (!WriteUint8((uint8_t)str.GetLength()))
// 		return false;
// 	unsigned int size = str.GetLength() * sizeof(char);
// 	if (Write(str.c_str(), size) != size)
// 		return false;
// 	return true;
// }
// 
// bool CMemStream::WriteShortCString(const CString& str)
// {
// 	if (str.GetLength() > 0xffff)
// 		return false;
// 	if (!WriteUint16((uint16_t)str.GetLength()))
// 		return false;
// 	unsigned int size = str.GetLength() * sizeof(char);
// 	if (Write(str.c_str(), size) != size)
// 		return false;
// 	return true;
// }

// bool CMemStream::WriteCString(const CString& str)
// {
// 	if (!WriteUint32((uint32_t)str.GetLength()))
// 		return false;
// 	unsigned int size = str.GetLength() * sizeof(char);
// 	if (Write(str.c_str(), size) != size)
// 		return false;
// 	return true;
// }
